#include "float.h"
#include <sstream>
Length::Length(Float inval, string inunits)
{
	try {
		this->value = inval.value; 
		if(inunits == "kilometers") {
			units = kilometers;
		} else if(inunits == "meters") {
			units = meters;
		} else if(inunits == "centimeters") {
			units = centimeters;
		} else if(inunits == "millimeters") {
			units = millimeters;
		} else if(inunits == "miles") {
			units = miles;
		} else if(inunits == "yards") {
			units = yards;
		} else if(inunits == "feet") {
			units = feet;
		} else if(inunits == "inches") {
			units = inches;
		} else {
			std::cerr << "Un-known length value! assuming meters." << std::endl;
			units = meters;
		} 
	} catch (...) {
		std::cerr << "couldn't initialize Length!" << std::endl;
		throw "Couldn't initialize the Length class!"; 
	} 
} 
Length::Length()
{
	this->value = 0.0; 
	units = meters;
}
//the to<Unit> functions are based on the premise that x * y/x = y, so xfeet/1 * meters/yfeet = xmeters
Length Length::toMeters() const
{	
	Length tmp = *this;
	 switch (units) {			//what units are we in?
		case meters:	
			break;	//we're already in meters - so just return a reference to self.
		case miles:
			tmp *= 1609.344;	//1609.344 meters per mile
			break;
		case yards:
			tmp *= 0.9144;		//0.9144 meters per yard
			break;
		case feet:
			tmp *= 0.3048;		//0.3048 meters per foot 
			break;
		case inches:
			tmp *= 0.0254;		//0.0254 meters per inch
			break;
		case kilometers:
			tmp *= 1000;	
			break;
		case centimeters:
			tmp *= 0.01; 		
			break;
		case millimeters:
			tmp *= 0.001; 		
			break;
		default:
			cerr << "Error while trying to convert to meters!" << endl;
			break;
		
	} 
	tmp.units = meters;
	return tmp;
} 
Length Length::toFeet()
{	
	Length tmp = *this;
	 switch (units) {			//what units are we in?
		case feet:	
			break;	
		case miles:
			tmp *= 5280.0;	
			break;
		case yards:
			tmp *= 3.0;		
			break;
		case inches:
			tmp *= 0.083333;	
			break;
		case kilometers:
			tmp *= 3280.8399;	
			break;
		case meters:
			tmp *= 3.2808399; 
			break;
		case centimeters:
			tmp *= 0.032808399; 		
			break;
		case millimeters:
			tmp *= 0.0032808399; 		
			break;
		default:
			cerr << "Error while trying to convert to meters!" << endl;
			break;
		
	} 
	tmp.units = feet;
	return tmp;
} 
Length Length::toInches()
{	
	Length tmp = *this;
	 switch (units) {			//what units are we in?
		case inches:	
			break;	
		case miles:
			tmp *= 63360.0;	
			break;
		case yards:
			tmp *= 36.0;		
			break;
		case feet:
			tmp *= 12.0;	
			break;
		case kilometers:
			tmp *= 39370.0787;	
			break;
		case meters:
			tmp *= 39.3700787; 
			break;
		case centimeters:
			tmp *= 0.393700787; 		
			break;
		case millimeters:
			tmp *= 0.0393700787; 		
			break;
		default:
			cerr << "Error while trying to convert to meters!" << endl;
			break;
		
	} 
	tmp.units = inches;
	return tmp;
} 
Length Length::toYards()
{	
	Length tmp = *this;
	 switch (units) {			//what units are we in?
		case yards:	
			break;	
		case miles:
			tmp *= 1760;	
			break;
		case inches:
			tmp *= 0.02777777;		
			break;
		case feet:
			tmp *= 0.33333333;	
			break;
		case kilometers:
			tmp *= 1093.6133;	
			break;
		case meters:
			tmp *= 1.0936133; 
			break;
		case centimeters:
			tmp *= 0.010936133; 		
			break;
		case millimeters:
			tmp *= 0.0010936133; 		
			break;
		default:
			cerr << "Error while trying to convert to meters!" << endl;
			break;
		
	} 
	tmp.units = yards;
	return tmp;
} 
Length Length::toKilometers()
{
  Length tmp = *this;
	 switch (units) {			//what units are we in?
		case kilometers:	
			break;	
		case miles:
			tmp *= 1.609344;	
			break;
		case yards:
			tmp *= 9.144e-4;		
			break;
		case feet:
		        tmp *= 3.048e-4;
			break;
		case inches:
		        tmp *= 2.54e-5;	
			break;
		case meters:
			tmp *= 1.0e-3; 
			break;
		case centimeters:
			tmp *= 1.0e-5; 		
			break;
		case millimeters:
			tmp *= 1.0e-6; 		
			break;
		default:
			cerr << "Error while trying to convert to meters!" << endl;
			break;
		
	} 
	tmp.units = kilometers;
	return tmp;
}
Length Length::toCentimeters()
{ 
Length tmp = *this;
	 switch (units) {			//what units are we in?
		case centimeters:	
			break;	
		case miles:
			tmp *= 160934.4;	
			break;
		case yards:
			tmp *= 91.44;		
			break;
		case feet:
		        tmp *= 30.48;
			break;
		case inches:
		        tmp *= 2.54;	
			break;
		case meters:
			tmp *= 1.0e2; 
			break;
		case kilometers:
			tmp *= 1.0e5; 		
			break;
		case millimeters:
			tmp *= 1.0e-1; 		
			break;
		default:
			cerr << "Error while trying to convert to meters!" << endl;
			break;
		
	} 
	tmp.units = centimeters;
	return tmp;
}
Length Length::toMillimeters()
{
   Length tmp = *this;
	 switch (units) {			//what units are we in?
		case millimeters:	
			break;	
		case miles:
			tmp *= 1609344;	
			break;
		case yards:
			tmp *= 914.4;		
			break;
		case feet:
		        tmp *= 304.8;
			break;
		case inches:
		        tmp *= 25.4;	
			break;
		case meters:
			tmp *= 1.0e3; 
			break;
		case kilometers:
			tmp *= 1.0e6; 		
			break;
		case centimeters:
			tmp *= 10; 		
			break;
		default:
			cerr << "Error while trying to convert to meters!" << endl;
			break;
		
	} 
	tmp.units = millimeters;
	return tmp;
}
Length Length::toMiles()
{
  Length tmp = *this;
	 switch (units) {			//what units are we in?
		case miles:	
			break;	
		case centimeters:
			tmp *= 6.21371192e-6;	
			break;
		case yards:
			tmp *= 5.68181818e-4;		
			break;
		case feet:
		        tmp *= 1.893939e-4;
			break;
		case inches:
		        tmp *= 1.57828283e-5;	
			break;
		case meters:
			tmp *= 6.21371192e-4; 
			break;
		case kilometers:
			tmp *= 0.621371192; 		
			break;
		case millimeters:
			tmp *= 6.21371192e-7; 		
			break;
		default:
			cerr << "Error while trying to convert to meters!" << endl;
			break;
		
	} 
	tmp.units = miles;
	return tmp;
}
string Length::toString()
{
 string unitStr;
 switch (units) {			//what units are we in?
		case meters:	
			unitStr = "meters";
			break;	       //we're already in meters - so just return a reference to self.
		case miles:
			unitStr = "miles";	
			break;
		case yards:
			unitStr = "yards";		
			break;
		case feet:
			unitStr = "feet";		
			break;
		case inches:
			unitStr = "inches";		
			break;
		case kilometers:
			unitStr = "kilometers";	
			break;
		case centimeters:
			unitStr = "centimeters"; 		
			break;
		case millimeters:
			unitStr = "millimeters"; 		
			break;
		default:
			unitStr = "NIL"; 
			break;
		
	} 
	string newStr; 
	stringstream theStream; 
	theStream << value <<" " << unitStr; 
	newStr = theStream.str();
	return newStr;
} 
Length Length::operator=(Length in)
{
	if(&in == this) {			//check for self-assingment
		return *this;			//if in is the same as myself, return myself, and do nothing else.
	} else {
		this->value = in.value;
		this->units = in.units;
		return *this;
	} 
}
Length Length::operator=(const Float in)
{
	this->value = in.value;
	return *this;
} 
Length Length::operator=(const double in)
{
	this->value = in;
	return *this;
} 
Length Length::operator=(const int in)
{
	this->value = (double)in;
	return *this;
} 

bool Length::operator==(const Length in)
{
  Float tmp = this->toMeters();
  Float tmp2 = in.toMeters();
  if(tmp == tmp2) {
    return true;
  } else {
    return false;
  } 
  
} 
